//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Workflow.ComponentModel;
using System.Collections;
using System.Collections.Specialized;
using System.Workflow.ComponentModel.Design;
using System.ComponentModel.Design;
using Microsoft.Research.ScientificWorkflow.NetCDF.CSharpAPI;
using System.Workflow.ComponentModel.Compiler;

using Microsoft.Research.ScientificWorkflow;
using Microsoft.Research.ScientificWorkflow.Helpers;

using WorkflowData;

namespace Microsoft.Research.ScientificWorkflow.Activities
{
    [Name("Hyper Cube Filter Transform")]
    [WorkflowCategory("NetCdf")]
    [Description("Filters out all the data in a HyperCube structure whose variable name does not match those given by an input parameter")]
	public class HyperCubeVariableFilter : Activity
	{
		#region Property Definitions

		public static DependencyProperty IncludedVariableNamesProperty =
		    DependencyProperty.Register("IncludedVariableNames", typeof(string), typeof(HyperCubeVariableFilter));


		[RequiredInputParam]
		[Name("Variable Names", "Variables to Include")]
		[Description("The list of variable names whose data will still be included in the output data. Any data whose variable name is not contained in this list will be discarded.")]
		public string IncludedVariableNames
		{
			get { return ((string)(base.GetValue(HyperCubeVariableFilter.IncludedVariableNamesProperty))); }
			set { base.SetValue(HyperCubeVariableFilter.IncludedVariableNamesProperty, value); }
		}

        public static DependencyProperty InputCubeProperty =
            DependencyProperty.Register("InputCube", typeof(HyperCube), typeof(HyperCubeVariableFilter));

        [RequiredInputParam]
        [Name("In Hyper Cube")]
        [Description("Hyper Cube for Filter")]
        public HyperCube InputCube
        {
            get
            {
                return ((HyperCube)(base.GetValue(HyperCubeVariableFilter.InputCubeProperty)));
            }
            set
            {
                base.SetValue(HyperCubeVariableFilter.InputCubeProperty, value);
            }
        }

        public static DependencyProperty FilteredCubeProperty =
            DependencyProperty.Register("FilteredCube", typeof(HyperCube), typeof(HyperCubeVariableFilter));

        [OutputParam]
        [Name("Resultant Hyper Cube")]
        [Description("Filtered cube containing data with variable filtered.")]
        public HyperCube FilteredCube
        {
            get
            {
                return ((HyperCube)(base.GetValue(HyperCubeVariableFilter.FilteredCubeProperty)));
            }
            set
            {
                base.SetValue(HyperCubeVariableFilter.FilteredCubeProperty, value);
            }
        }
		
        #endregion

		public HyperCubeVariableFilter()
			: base()
		{
		}

        private string[] varNameArray;

        private void ParseVariables()
        {
            string[] v = (this.IncludedVariableNames).Split(new char[] { ',' });
            if (v.Length == 0)
                throw new ArgumentException("Error Converting from String to Variable Array");

            this.varNameArray = new string[v.Length];
            for (int i = 0; i < v.Length; i++)
            {
                this.varNameArray[i] = v[i];
            }
        }

        protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
		{
            ParseVariables();

            if (this.varNameArray.Length == 0)
				throw new ArgumentException("You need to provide at least one variable.", "IncludedVariableNames");

			HyperCubeSchema oldSchema = this.InputCube.Schema;
            HyperCubeSchema resultSchema = new HyperCubeSchema(this.varNameArray.Length);

            for (int i = 0; i < this.varNameArray.Length; i++)
			{
                resultSchema.SetName(i, this.varNameArray[i]);
				int oldIndex = oldSchema.LookupColumnIndex(this.varNameArray[i]);
				if (oldIndex >= 0)
				{
					resultSchema.SetType(i, oldSchema.GetColumnType(oldIndex));
				}
                if (oldSchema.HasDimension(oldIndex, HyperCube.Axis.X))
                    resultSchema.SetDimension(i, HyperCube.Axis.X);
                if (oldSchema.HasDimension(oldIndex, HyperCube.Axis.Y))
                    resultSchema.SetDimension(i, HyperCube.Axis.Y);
                if (oldSchema.HasDimension(oldIndex, HyperCube.Axis.Z))
                    resultSchema.SetDimension(i, HyperCube.Axis.Z);
                if (oldSchema.HasDimension(oldIndex, HyperCube.Axis.T))
                    resultSchema.SetDimension(i, HyperCube.Axis.T);
			}

            // Copy the dimension names and types.
            resultSchema.XDimensionName = oldSchema.XDimensionName;
            resultSchema.XDimensionType = oldSchema.XDimensionType;

            resultSchema.YDimensionName = oldSchema.YDimensionName;
            resultSchema.YDimensionType = oldSchema.YDimensionType;

            resultSchema.ZDimensionName = oldSchema.ZDimensionName;
            resultSchema.ZDimensionType = oldSchema.ZDimensionType;

            resultSchema.TDimensionName = oldSchema.TDimensionName;
            resultSchema.TDimensionType = oldSchema.TDimensionType;

			// Determine the size of the HyperCube
			int xLen = this.InputCube.GetDataAxisLength(HyperCube.Axis.X);
            int yLen = this.InputCube.GetDataAxisLength(HyperCube.Axis.Y);
            int zLen = this.InputCube.GetDataAxisLength(HyperCube.Axis.Z);
            int tLen = this.InputCube.GetDataAxisLength(HyperCube.Axis.T);

			HyperCube result = new HyperCube(resultSchema, xLen, yLen, zLen, tLen);
            result.XDimensionValues = this.InputCube.XDimensionValues;
            result.YDimensionValues = this.InputCube.YDimensionValues;
            result.ZDimensionValues = this.InputCube.ZDimensionValues;
            result.TDimensionValues = this.InputCube.TDimensionValues;


			// Populate the data in the output HyperCube
            for (int i = 0; i < this.varNameArray.Length; i++)
			{
				int oldIndex = oldSchema.LookupColumnIndex(this.varNameArray[i]);
				if (oldIndex >= 0)
				{
					for (int x = 0; x < xLen; x++)
					{
						for (int y = 0; y < yLen; y++)
						{
							for (int z = 0; z < zLen; z++)
							{
								for (int t = 0; t < tLen; t++)
								{
                                    HyperCubeDataPoint data = this.InputCube[x, y, z, t];
									if ( data != null && data[oldIndex] != null )
										result.SetData(data[oldIndex], x, y, z, t, i);
								}
							}
						}
					}
				}
			}

			//the original hypercube is modified (input hypecube is replaced).
            this.FilteredCube = result;
			return ActivityExecutionStatus.Closed;
		}
	}
}
